package network_programming.np1_socket_and_serversocket;

import java.io.IOException;
import java.net.*;

/**
 * ServerSocket选项
 * SO_TIMEOUT       表示等待客户连接的超时时间，以毫秒为单位，如设为0则表示永远不会超时，默认值为0
 *      设置该选项：public void setSoTimeout(int timeout) throws SocketException
 *      获取该选项：public int getSoTimeout() throws IOException
 * SO_REUSEADDR     表示是否允许重用服务器所绑定的地址，需在ServerSocket绑定端口之前设置
 *      设置该选项：public void setResuseAddress(boolean on) throws SocketException
 *      获取该选项：public boolean getResuseAddress() throws SocketException
 * SO_RCVBUF        表示接收数据的缓冲区大小，以字节为单位
 *      设置该选项：public void setReceiveBufferSize(int size) throws SocketException
 *      获取该选项：public void getReceiveBufferSize() throws SocketException
 *      setReceiveBufferSize在ServerSocket被绑定到特定端口之前或之后调用都有效
 *      例外情况：要设置大于64k的缓冲区时，则必须在ServerSocket被绑定到特定端口之前设置才有效
 *
 * 设置连接时间、延迟和带宽的相对重要性
 * public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
 * 该方法的作用与Socket的setPerformancePreferences()方法的作用相同
 * */
public class ServerSocketDemo {
    private ServerSocket serverSocket;
    // 不带参数的默认构造方法，不与任何端口绑定，需要通过bind()方法与特定端口绑定
    // 用途：允许服务器在绑定到特定端口之前设置ServerSocket的一些选项，有些选项在服务器绑定端口后就不能再改变
    public ServerSocketDemo() throws IOException {
        serverSocket = new ServerSocket();
    }
    public ServerSocketDemo(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    // 参数port指定服务器绑定（监听）的端口，参数backlog指定客户连接请求队列的长度
    // 当参数port设为0时，表示由操作系统为服务器分配一个任意的可用端口，也被称为匿名端口
    public ServerSocketDemo(int port, int backlog) throws IOException {
        serverSocket = new ServerSocket(port, backlog);
    }
    // 主机只有一个ip地址时，默认情况下服务器与该ip绑定
    // 参数bindAddress指定服务器绑定的ip地址
    public ServerSocketDemo(int port, int backlog, InetAddress bindAddress) throws IOException {
        serverSocket = new ServerSocket(port, backlog, bindAddress);
    }

    public void service() throws IOException {
        InetAddress address = serverSocket.getInetAddress();    // 获取服务器绑定的ip地址
        int port = serverSocket.getLocalPort();     // 获取服务器绑定的端口

        serverSocket.setSoTimeout(1000*60);     // 设置等待客户连接时间不超过1分钟

        Socket socket = serverSocket.accept();  // 阻塞，等待客户连接

        serverSocket.close();      // 关闭ServerSocket，释放资源
        // 当服务器程序运行结束时，即使没有执行ServerSocket.close()方法，操作系统也会释放这个服务器所占用的端口
        if(serverSocket.isClosed()) // isClosed()方法只有当执行了ServerSocket.close()方法后才返回ture，否则返回false
            System.out.println("ServerSocket已关闭");
        if(serverSocket.isBound())  // isBound()方法只要ServerSocket绑定一个端口后即使被关闭了也返回ture
            System.out.println("ServerSocket已绑定一个端口");
        if(serverSocket.isBound() && serverSocket.isClosed())
            System.out.println("ServerSocket已绑定特定端口并并且还没有被关闭");
    }

    public static void main(String[] args) throws IOException {
//        ServerSocketDemo ssd = new ServerSocketDemo(8000, 10, InetAddress.getByName("192.168.16.1"));
        ServerSocketDemo ssd = new ServerSocketDemo();
        if(!ssd.serverSocket.getReuseAddress())
            ssd.serverSocket.setReuseAddress(true);    // 允许重用Socket所绑定的本地地址
        ssd.serverSocket.bind(new InetSocketAddress(8000));    // 绑定端口
        ssd.service();
    }
}
